home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / The Hacks / GrowBoxDock / Sources / TextDrag.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-30  |  23.1 KB  |  826 lines

  1. /*
  2.     File:        TextDrag.c
  3.  
  4.     Contains:    Text document dragging support for SimpleText
  5.  
  6.     Version:    Mac OS X
  7.  
  8.     Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  9.                 ("Apple") in consideration of your agreement to the following terms, and your
  10.                 use, installation, modification or redistribution of this Apple software
  11.                 constitutes acceptance of these terms.  If you do not agree with these terms,
  12.                 please do not use, install, modify or redistribute this Apple software.
  13.  
  14.                 In consideration of your agreement to abide by the following terms, and subject
  15.                 to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
  16.                 copyrights in this original Apple software (the "Apple Software"), to use,
  17.                 reproduce, modify and redistribute the Apple Software, with or without
  18.                 modifications, in source and/or binary forms; provided that if you redistribute
  19.                 the Apple Software in its entirety and without modifications, you must retain
  20.                 this notice and the following text and disclaimers in all such redistributions of
  21.                 the Apple Software.  Neither the name, trademarks, service marks or logos of
  22.                 Apple Computer, Inc. may be used to endorse or promote products derived from the
  23.                 Apple Software without specific prior written permission from Apple.  Except as
  24.                 expressly stated in this notice, no other rights or licenses, express or implied,
  25.                 are granted by Apple herein, including but not limited to any patent rights that
  26.                 may be infringed by your derivative works or by other works in which the Apple
  27.                 Software may be incorporated.
  28.  
  29.                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  30.                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  31.                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  32.                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  33.                 COMBINATION WITH YOUR PRODUCTS.
  34.  
  35.                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  36.                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  37.                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  38.                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  39.                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  40.                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  41.                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  
  43.     Copyright © 1993-2001 Apple Computer, Inc., All Rights Reserved
  44. */
  45.  
  46. #include "MacIncludes.h"
  47.  
  48. #include "TextFile.h"
  49.  
  50. // --------------------------------------------------------------------------------------------------------------
  51. // FORWARD EXTERNAL DECLARES
  52. // --------------------------------------------------------------------------------------------------------------
  53.  
  54. extern Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType);
  55. extern Boolean IsDropInFinderTrash(AEDesc *dropLocation);
  56. extern OSStatus SaveCurrentUndoState(WindowDataPtr pData, short newCommandID);
  57.  
  58. // --------------------------------------------------------------------------------------------------------------
  59. // INTERNAL DEFINES
  60. // --------------------------------------------------------------------------------------------------------------
  61.  
  62. // Dragging private globals
  63.  
  64. extern Boolean    gCanAccept;
  65. extern Boolean    gTE6Version;
  66.  
  67. // --------------------------------------------------------------------------------------------------------------
  68. // GLOBALS USED ONLY BY THESE ROUTINES
  69. // --------------------------------------------------------------------------------------------------------------
  70.  
  71. static Boolean    gCaretShow;
  72. static long        gCaretTime;
  73. static short    gCaretOffset, gLastOffset, gInsertPosition;
  74. static Boolean    gCursorInContent;
  75.  
  76. static unsigned long gAutoScrollTicks;
  77.  
  78. // --------------------------------------------------------------------------------------------------------------
  79. // INTERNAL ROUTINES
  80. // --------------------------------------------------------------------------------------------------------------
  81.  
  82. extern void AdjustTE(WindowDataPtr pData, Boolean doScroll);
  83.  
  84. // --------------------------------------------------------------------------------------------------------------
  85. //
  86. // GetSelectionSize -
  87. //
  88.  
  89. static short GetSelectionSize(TEHandle hTE)
  90. {
  91.  
  92.     return((**(hTE)).selEnd - (**(hTE)).selStart);
  93.  
  94. } // GetSelectionSize
  95.  
  96.  
  97. // --------------------------------------------------------------------------------------------------------------
  98. //
  99. // GetSelectedTextPtr
  100. //
  101.  
  102. static Ptr GetSelectedTextPtr(TEHandle hTE)
  103. {
  104.  
  105.     return((*(**(hTE)).hText) + (**(hTE)).selStart);
  106.  
  107. } // GetSelectedTextPtr
  108.  
  109.  
  110. // --------------------------------------------------------------------------------------------------------------
  111. //
  112. //    TEIsFrontOfLine - Given a text offset and a TextEdit handle, returns true if the given
  113. //                      offset is at the beginning of a line start.
  114. //
  115.  
  116. static short TEIsFrontOfLine(short textOffset, TEHandle hTE)
  117. {
  118.  
  119.     short theLine = 0;
  120.  
  121.     if ((**hTE).teLength == 0)
  122.         return true;
  123.  
  124.     if (textOffset >= (**hTE).teLength)
  125.         return( (*((**hTE).hText))[(**hTE).teLength - 1] == 0x0D );
  126.  
  127.     while ((**hTE).lineStarts[theLine] < textOffset)
  128.         theLine++;
  129.  
  130.     return( (**hTE).lineStarts[theLine] == textOffset );
  131.  
  132. } // TEIsFrontOfLine
  133.  
  134.  
  135. // --------------------------------------------------------------------------------------------------------------
  136. //
  137. //    TEGetLine - Given an offset and a TextEdit handle, returns the line number that contains the offset.
  138. //
  139.  
  140. static short TEGetLine(short textOffset, TEHandle hTE)
  141. {    
  142.  
  143.     short theLine = 0;
  144.  
  145.     if (textOffset > (**hTE).teLength)
  146.         return((**hTE).nLines);
  147.     else
  148.         {
  149.         while ((**hTE).lineStarts[theLine] <= textOffset)
  150.             ++theLine;
  151.         }
  152.         
  153.     return theLine;
  154.  
  155. } // TEGetLine
  156.  
  157.  
  158. // --------------------------------------------------------------------------------------------------------------
  159. //
  160. //    DrawCaret - Draws a caret in a TextEdit field at the given offset by inverting the image of the
  161. //                caret onto the screen. DrawCaret expects the port to be set to the port that the
  162. //                TextEdit record is in.
  163. //
  164.  
  165. static void DrawCaret(short textOffset, TEHandle hTE)
  166. {    
  167.  
  168.     Point theLoc;
  169.     short lineHeight, theLine;
  170.  
  171.     //
  172.     //    Get the coordinates and the line of the offset to draw the caret.
  173.     //
  174.  
  175.     theLoc  = TEGetPoint(textOffset, hTE);
  176.     theLine = TEGetLine(textOffset, hTE);
  177.     
  178.     if (!gTE6Version)
  179.         {
  180.         // %%% Most heinously bogus - 21-Dec-93 JM3
  181.         //
  182.         //    For some reason, TextEdit dosen't return the proper coordinates
  183.         //    of the last offset in the field if the last character in the record
  184.         //    is a carriage return. TEGetPoint returns a point that is one line
  185.         //    higher than expected. The following code fixes this problem.
  186.     
  187.         // It has also been fixed for TE6
  188.         
  189.         if ((textOffset == (**hTE).teLength) && (*((**hTE).hText))[(**hTE).teLength - 1] == 0x0D)
  190.             theLoc.v += TEGetHeight(theLine, theLine, hTE);
  191.         }
  192.     
  193.     //
  194.     //    Always invert the caret when drawing.
  195.     //
  196.  
  197.     PenMode(patXor);
  198.  
  199.     //
  200.     //    Get the height of the line that the offset points to.
  201.     //
  202.  
  203.     lineHeight = TEGetHeight(theLine, theLine, hTE);
  204.  
  205.     //
  206.     //    Draw the appropriate caret image.
  207.     //
  208.  
  209.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  210.     Line(0, 1 - lineHeight);
  211.  
  212.     PenNormal();
  213.  
  214. } // DrawCaret
  215.  
  216.  
  217. // --------------------------------------------------------------------------------------------------------------
  218. //
  219. //    HitTest - Given a point in global coordinates, HitTest returns an offset into the text if the
  220. //              point is inside the given TERecord. If the point is not in the text, HitTest returns
  221. //              -1.
  222. //
  223.  
  224. static short HitTest(Point theLoc, TEHandle hTE)
  225. {    
  226.  
  227.     WindowPtr    theWindow;
  228.     short        textOffset = -1;
  229.  
  230.     if (FindWindow(theLoc, &theWindow) == inContent)
  231.         {
  232.         SetPortWindowPort (theWindow);
  233.         GlobalToLocal(&theLoc);
  234.  
  235.         if (PtInRect(theLoc, &((** hTE).viewRect)) && PtInRect(theLoc, &((** hTE).viewRect)))
  236.             {
  237.             textOffset = TEGetOffset(theLoc, hTE);
  238.  
  239.             if ((TEIsFrontOfLine(textOffset, hTE)) && (textOffset) &&
  240.                 ((*((** hTE).hText))[textOffset - 1] != 0x0D) &&
  241.                 (TEGetPoint(textOffset - 1, hTE).h < theLoc.h))
  242.                 {
  243.                 --textOffset;
  244.                 }
  245.             }
  246.         }
  247.  
  248.     return textOffset;
  249.  
  250. } // HitTest
  251.  
  252.  
  253. // --------------------------------------------------------------------------------------------------------------
  254. //
  255. // GetCharAtOffset - Given a text offset and a TEHandle, returns the character located at that offset in
  256. //                     the TERecord.
  257. //
  258.  
  259. static char GetCharAtOffset(short offset, TEHandle hTE)
  260. {
  261.  
  262.     if (offset < 0)
  263.         return 0x0D;
  264.  
  265.     return(((char *) *((**hTE).hText))[offset]);
  266.  
  267. } // GetCharAtOffset
  268.  
  269.  
  270. // --------------------------------------------------------------------------------------------------------------
  271. //
  272. // WhiteSpace - Determines if the input character is white space.
  273. //
  274.  
  275. static Boolean WhiteSpace(char theChar)
  276. {
  277.  
  278.     return((theChar == ' ') || (theChar == 0x0D));
  279.  
  280. } // WhiteSpace
  281.  
  282.  
  283. // --------------------------------------------------------------------------------------------------------------
  284. //
  285. // WhiteSpaceAtOffset - Given a text offset into a TERecord, determines if the character at that location is
  286. //                        whitespace.
  287. //
  288.  
  289. static Boolean WhiteSpaceAtOffset(short offset, TEHandle hTE)
  290. {
  291.  
  292.     char theChar;
  293.  
  294.     if ((offset < 0) || (offset > (**hTE).teLength - 1))
  295.         return true;
  296.  
  297.     theChar = ((char *) *((**hTE).hText))[offset];
  298.     return(WhiteSpace(theChar));
  299.  
  300. } // WhiteSpaceAtOffset
  301.  
  302.  
  303. // --------------------------------------------------------------------------------------------------------------
  304. //
  305. // InsertTextAtOffset -
  306. //
  307.  
  308. static short InsertTextAtOffset(short textOffset, char *theBuf, long textSize, StScrpHandle styleHand, TEHandle hTE)
  309. {
  310.     short    charactersAdded = 0;
  311.     
  312.     if (textSize == 0)
  313.         return charactersAdded;
  314.  
  315.     //    If we're inserting at the end of a word and the selection does not begin with
  316.     //    a space, insert a space before the insertion.
  317.  
  318.     if (!WhiteSpaceAtOffset(textOffset - 1, hTE) &&
  319.          WhiteSpaceAtOffset(textOffset, hTE) &&
  320.         !WhiteSpace(theBuf[0]))
  321.         {
  322.  
  323.         TESetSelect(textOffset, textOffset, hTE);
  324.         TEKey(' ', hTE);
  325.         ++textOffset;
  326.         ++charactersAdded;
  327.         }
  328.  
  329.     //    If we're inserting at the beginning of a word and the selection does not end
  330.     //    with a space, insert a space after the insertion.
  331.  
  332.     if ( WhiteSpaceAtOffset(textOffset - 1, hTE) &&
  333.         !WhiteSpaceAtOffset(textOffset, hTE) &&
  334.         !WhiteSpace(theBuf[textSize - 1]))
  335.         {
  336.  
  337.         TESetSelect(textOffset, textOffset, hTE);
  338.         TEKey(' ', hTE);
  339.         ++charactersAdded;
  340.         }
  341.  
  342.     // Before we insert this text, make sure we set the selection range to a single character.
  343.     // This assures that we won't overwrite the text in the previous selection.
  344.     
  345.     TESetSelect(textOffset, textOffset, hTE);
  346.     TEStyleInsert(theBuf, textSize, styleHand, hTE);
  347.  
  348.     return charactersAdded;
  349.     
  350. } // InsertTextAtOffset
  351.  
  352. // --------------------------------------------------------------------------------------------------------------
  353. // OOP INTERFACE ROUTINES
  354. // --------------------------------------------------------------------------------------------------------------
  355.  
  356. OSStatus TextDragTracking(WindowPtr pWindow, void *pData, DragReference theDragRef, short message)
  357. {
  358. #pragma unused(pWindow)
  359.  
  360.     unsigned long    attributes;
  361.     RgnHandle        hilightRgn;
  362.     Point            localMouseLoc, dragMouseLoc;
  363.     short            textOffset;
  364.     long            theTime = TickCount();
  365.     WindowDataPtr    theData = (WindowDataPtr) pData;
  366.     Rect             pRect; // (DP) Needed because of Opacity Issues
  367.  
  368.  
  369.     GetDragAttributes(theDragRef, &attributes);
  370.  
  371.     switch(message)
  372.         {
  373.         case kDragTrackingEnterWindow:
  374.  
  375.             gCanAccept = IsOnlyThisFlavor(theDragRef, 'TEXT');
  376.  
  377.             gCaretTime   = theTime;
  378.             gCaretOffset = gLastOffset = -1;
  379.             gCaretShow   = true;
  380.  
  381.             gCursorInContent = false;
  382.             gAutoScrollTicks = 0;
  383.  
  384.             break;        
  385.  
  386.         case kDragTrackingInWindow:
  387.  
  388.             if (gCanAccept)
  389.                 {            
  390.                 GetDragMouse(theDragRef, &dragMouseLoc, 0L);
  391.                 localMouseLoc = dragMouseLoc;
  392.                 GlobalToLocal(&localMouseLoc);
  393.  
  394.                 if (attributes & kDragInsideSenderWindow)
  395.                     {
  396.                     short deltaV = 0;
  397.  
  398.                     if ((localMouseLoc.v < 16) && (localMouseLoc.v > 0))
  399.                         deltaV = theData->vScrollAmount;
  400.  
  401.                     if (localMouseLoc.v > GetPortBounds(GetWindowPort(pWindow), &pRect)->bottom - 16)
  402.                         deltaV -= theData->vScrollAmount;
  403.  
  404.                     if (deltaV == 0)
  405.                         {
  406.                         gAutoScrollTicks = 0;
  407.                         }
  408.                     else
  409.                         {
  410.                         if (gAutoScrollTicks == 0)
  411.                             {
  412.                             gAutoScrollTicks = theTime;
  413.                             }
  414.                         else
  415.                             {
  416.                             if (theTime - gAutoScrollTicks > 10)    // 10 ticks to start is what the H.I. doc says
  417.                                 {
  418.                                 // remove the drag-destination caret if it's showing
  419.                                 if (gCaretOffset != -1)
  420.                                     {
  421.                                     DrawCaret(gCaretOffset, ((TextDataPtr) pData)->hTE);
  422.                                     gCaretOffset = -1;
  423.                                     }
  424.     
  425.                                 SetControlAndClipAmount(theData->vScroll, &deltaV);
  426.                                 if (deltaV != 0)
  427.                                     {
  428.                                     DragPreScroll(theDragRef, 0, deltaV);
  429.                                     DoScrollContent(pWindow, theData, 0, deltaV);
  430.                                     DragPostScroll(theDragRef);
  431.                                     }
  432.                                 
  433.                                 gAutoScrollTicks = theTime - 7;    // let's wait 3 more ticks until next jump
  434.                                 }
  435.                             }
  436.                         }
  437.                     }
  438.  
  439.                 if (attributes & kDragHasLeftSenderWindow)
  440.                     {
  441.                     if (PtInRect(localMouseLoc, &(theData->contentRect)))
  442.                         {
  443.                         if (!gCursorInContent)
  444.                             {
  445.                             hilightRgn = NewRgn();
  446.                             RectRgn(hilightRgn, &theData->contentRect);
  447.                             ShowDragHilite(theDragRef, hilightRgn, true);                    
  448.                             DisposeRgn(hilightRgn);
  449.                             }
  450.                         gCursorInContent = true;
  451.                         }
  452.                     else
  453.                         {
  454.                         if (gCursorInContent)
  455.                             {
  456.                             HideDragHilite(theDragRef);
  457.                             gCursorInContent = false;
  458.                             }
  459.                         }
  460.                     }
  461.                 }
  462.  
  463.             textOffset = HitTest(dragMouseLoc, ((TextDataPtr)pData)->hTE);
  464.  
  465.             //    If this application is the sender, do not allow tracking through
  466.             //    the selection in the window that sourced the drag.
  467.  
  468.             if (attributes & kDragInsideSenderWindow)
  469.                 {
  470.                 if ((textOffset >= (**((TextDataPtr)pData)->hTE).selStart) &&
  471.                     (textOffset <= (**((TextDataPtr)pData)->hTE).selEnd))
  472.                     {
  473.                         textOffset = -1;
  474.                     }
  475.                 }
  476.  
  477.             gInsertPosition = textOffset;
  478.  
  479.             //    Reset the flashing counter if the offset has moved. This makes the
  480.             //    caret blink only after the caret has stopped moving long enough.
  481.  
  482.             if (textOffset != gLastOffset)
  483.                 {
  484.                 gCaretTime = theTime;
  485.                 gCaretShow = true;
  486.                 }
  487.             
  488.             gLastOffset = textOffset;
  489.  
  490.             //    Flash the caret, blinky-blinky-blinky.
  491.  
  492.             if (theTime - gCaretTime > GetCaretTime())
  493.                 {
  494.                 gCaretShow = !gCaretShow;
  495.                 gCaretTime = theTime;
  496.                 }
  497.             
  498.             if (!gCaretShow)
  499.                 textOffset = -1;
  500.  
  501.             //    If the caret offset has changed, move the caret on the screen.
  502.  
  503.             if (textOffset != gCaretOffset)
  504.                 {
  505.                 if (gCaretOffset != -1)
  506.                     DrawCaret(gCaretOffset, ((TextDataPtr)pData)->hTE);
  507.  
  508.                 if (textOffset != -1)
  509.                     DrawCaret(textOffset, ((TextDataPtr)pData)->hTE);
  510.                 }
  511.  
  512.             gCaretOffset = textOffset;
  513.             break;
  514.  
  515.         case kDragTrackingLeaveWindow:
  516.     
  517.             //    If the caret is on the screen, remove it.
  518.  
  519.             if (gCaretOffset != -1)
  520.                 {
  521.                 DrawCaret(gCaretOffset, ((TextDataPtr)pData)->hTE);
  522.                 gCaretOffset = -1;
  523.                 }
  524.  
  525.             // Remove the window hilighting, if any.
  526.     
  527.             if ((gCursorInContent) && (attributes & kDragHasLeftSenderWindow))
  528.                 HideDragHilite(theDragRef);
  529.  
  530.             break;
  531.             
  532.           } // switch (message)
  533.     
  534.     return noErr;
  535.  
  536. } // TextDragTracking
  537.  
  538.  
  539. // --------------------------------------------------------------------------------------------------------------
  540.  
  541. OSStatus TextDragReceive(WindowPtr pWindow, void *pData, DragReference theDragRef)
  542. {
  543.  
  544.     OSStatus            error;
  545.     unsigned short    items, index;
  546.     DragAttributes    attributes;
  547.     ItemReference    theItem;
  548.     Ptr                textData;
  549.     StScrpHandle    styleHand;
  550.     Size            textSize, styleSize, totalTextSize;
  551.     short            mouseDownModifiers, mouseUpModifiers, moveText, selStart, selEnd;
  552.     long            totalTextStart;
  553.     long            additionalChars;
  554.     TEHandle        hTE;
  555.     Boolean            wasActive;
  556.     
  557.     if ((!gCanAccept)  || (gInsertPosition == -1))
  558.         return dragNotAcceptedErr;
  559.  
  560.     hTE = ((TextDataPtr) pData)->hTE;
  561.  
  562.     // We're going to try our best to insert some text, so first save off the beginning of where
  563.     // we'll do it.
  564.  
  565.     totalTextStart = gInsertPosition;
  566.     totalTextSize = 0L;
  567.  
  568.     // draw in this window, and activate the text editing record so that selections
  569.     // happen properly
  570.  
  571.     SetPortWindowPort (pWindow);
  572.  
  573.     wasActive = (*hTE)->active != 0;    // can't test window == FrontWindow (might not be front app)
  574.     if (!wasActive)
  575.         TEActivate(hTE);
  576.  
  577.     GetDragAttributes(theDragRef, &attributes);
  578.     GetDragModifiers(theDragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  579.  
  580.     moveText = (attributes & kDragInsideSenderWindow) &&
  581.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  582.  
  583.     //    Loop through all of the drag items contained in this drag and snag all of the 'TEXT'.
  584.  
  585.     CountDragItems(theDragRef, &items);
  586.     
  587.     for (index = 1; index <= items; index++)
  588.         {
  589.         //    Get the item's reference number, so we can refer to it.
  590.  
  591.         GetDragItemReferenceNumber(theDragRef, index, &theItem);
  592.  
  593.         //    Try to get the size for a 'TEXT' flavor. If this returns noErr,
  594.         //    then we know that a 'TEXT' flavor exists in the item.
  595.         
  596.         error = GetFlavorDataSize(theDragRef, theItem, 'TEXT', &textSize);
  597.  
  598.         if (error == noErr)
  599.             {        
  600.             // If the current length, plus the drag data would make the document too large, say so.
  601.  
  602.             if (((*hTE)->teLength + textSize) > kMaxLength)
  603.                 return eDocumentTooLarge;
  604.  
  605.             textData = NewPtr(textSize);
  606.     
  607.             // If we couldn't get a chunk of memory for the text, bail.
  608.  
  609.             if(textData == 0L)
  610.                 return memFullErr;
  611.  
  612.             GetFlavorData(theDragRef, theItem, 'TEXT', textData, &textSize, 0);
  613.  
  614.             // Let's see if there is an optional 'styl' flavor.
  615.  
  616.             styleHand = 0L;
  617.             
  618.             error = GetFlavorDataSize(theDragRef, theItem, 'styl', &styleSize);
  619.  
  620.             // If there was no 'styl' data, or it somehow was zero in length, don't
  621.             // attempt to insert it along with the text, 'cause we'd fail miserably.
  622.  
  623.             if ((error == noErr) && (styleSize != 0))
  624.                 {
  625.                 styleHand = (StScrpHandle) NewHandle(styleSize);
  626.             
  627.                 // If we couldn't get a chunk of memory for the styles, also bail.
  628.  
  629.                 if (styleHand == 0L)
  630.                     {    
  631.                     DisposePtr(textData);
  632.                     return memFullErr;
  633.                     }
  634.  
  635.                 HLock((Handle) styleHand);
  636.                 GetFlavorData(theDragRef, theItem, 'styl', *styleHand, &styleSize, 0L);
  637.                 HUnlock((Handle) styleHand);            
  638.                 }
  639.  
  640.             //    If the caret or highlighting is on the screen, remove it/them.
  641.     
  642.             if (gCaretOffset != -1)
  643.                 {
  644.                 DrawCaret(gCaretOffset, hTE);
  645.                 gCaretOffset = -1;
  646.                 }
  647.             
  648.             if (attributes & kDragHasLeftSenderWindow)
  649.                 HideDragHilite(theDragRef);
  650.  
  651.             // save away any changes, so that we can undo them
  652.             SaveCurrentUndoState(pData, cTypingCommand);
  653.             
  654.             // If this window is also the sender, delete the source selection text if the
  655.             // option key is not being held down.
  656.  
  657.             if (moveText)
  658.                 {
  659.                 selStart = (**hTE).selStart;
  660.                 selEnd   = (**hTE).selEnd;
  661.                 
  662.                 if ( WhiteSpaceAtOffset(selStart - 1, hTE) &&
  663.                     !WhiteSpaceAtOffset(selStart,     hTE) &&
  664.                     !WhiteSpaceAtOffset(selEnd - 1,   hTE) &&
  665.                      WhiteSpaceAtOffset(selEnd,       hTE))
  666.                     {
  667.                      
  668.                      if (GetCharAtOffset(selEnd, hTE) == ' ')
  669.                         (**hTE).selEnd++;
  670.                     }
  671.                 
  672.                 if (gInsertPosition > selStart)
  673.                     {
  674.                     selEnd = (**hTE).selEnd;
  675.                     gInsertPosition -= (selEnd - selStart);
  676.                     totalTextStart -= (selEnd - selStart);
  677.                     }
  678.                 
  679.                 TEDelete(hTE);    
  680.                 }
  681.  
  682.             // We can finally insert the text and style data into our record.
  683.  
  684.             additionalChars = InsertTextAtOffset(gInsertPosition, textData, textSize, styleHand, hTE);
  685.             
  686.             // In case we're inserting multiple chunks of text, we need to update the location of where we
  687.             // need to insert the next block.
  688.  
  689.             gInsertPosition += textSize + additionalChars;
  690.             totalTextSize += textSize + additionalChars;
  691.             
  692.             DisposePtr(textData);
  693.             if (styleHand)
  694.                 DisposeHandle((Handle) styleHand);
  695.             }
  696.         }
  697.  
  698.     // Select everything we've just inserted.
  699.  
  700.     TESetSelect(totalTextStart, totalTextStart + totalTextSize, hTE);
  701.  
  702.     AdjustTE(pData, false);
  703.     AdjustScrollBars(pWindow, false, false, nil);
  704.     
  705.     SetDocumentContentChanged( (WindowDataPtr) pData, true );
  706.  
  707.     // if we had to activate the edit record, deactivate it after we are all done
  708.     if (!wasActive)
  709.         TEDeactivate(hTE);
  710.  
  711.     return noErr;
  712.  
  713. } // TextDragReceive
  714.  
  715.  
  716. // --------------------------------------------------------------------------------------------------------------
  717.  
  718. Boolean DragText(WindowPtr pWindow, void *pData, EventRecord *pEvent, RgnHandle hilightRgn)
  719. {
  720.  
  721.     Point            theLoc = {0,0};
  722.     RgnHandle        dragRegion, tempRegion;
  723.     DragReference    theDragRef;
  724.     StScrpHandle    theStyleHand = 0L;
  725.     OSStatus            error;
  726.     AEDesc            dropLocation;
  727.     DragAttributes    attributes;
  728.     short            mouseDownModifiers, mouseUpModifiers, copyText;
  729.  
  730.     //    Copy the hilight region into dragRegion and offset it into global coordinates.
  731.  
  732.     CopyRgn(hilightRgn, dragRegion = NewRgn());
  733.     LocalToGlobal(&theLoc);
  734.     OffsetRgn(dragRegion, theLoc.h, theLoc.v);
  735.  
  736.     //    Wait for the mouse to move or the mouse button to be released. If the mouse button was
  737.     //    released before the mouse moves, return false. Returing false from DragText means that
  738.     //    a drag operation did not occur.
  739.  
  740.     if (!WaitMouseMoved(pEvent->where))
  741.         return false;
  742.  
  743.     NewDrag(&theDragRef);
  744.  
  745.     // add the flavor for the window title, errors can be ignored as this
  746.     // is a cosmetic addition
  747.     {
  748.     enum
  749.         {
  750.         kFlavorTypeClippingName = 'clnm'
  751.         };
  752.     Str255    windowTitle;
  753.     
  754.     GetWTitle(pWindow, windowTitle);
  755.     (void)     AddDragItemFlavor(theDragRef, 1, kFlavorTypeClippingName, &windowTitle, windowTitle[0]+1, flavorNotSaved);
  756.     }
  757.  
  758.     AddDragItemFlavor(theDragRef, 1, 'TEXT', GetSelectedTextPtr(((TextDataPtr)pData)->hTE), GetSelectionSize(((TextDataPtr)pData)->hTE), 0);
  759.  
  760.     theStyleHand = TEGetStyleScrapHandle(((TextDataPtr)pData)->hTE);
  761.  
  762.     // Just be a little paranoid and see if we did get a handle.
  763.  
  764.     if (theStyleHand)
  765.         {
  766.         HLock((Handle) theStyleHand);
  767.         AddDragItemFlavor(theDragRef, 1, 'styl', (Ptr) *theStyleHand, GetHandleSize((Handle) theStyleHand), 0);
  768.         DisposeHandle((Handle) theStyleHand);
  769.         }
  770.  
  771.     //    Set the item's bounding rectangle in global coordinates.
  772.         {
  773.         Rect    dragBounds;
  774.         
  775.         SetDragItemBounds(theDragRef, 1, GetRegionBounds( dragRegion, &dragBounds ));
  776.         }
  777.         
  778.     //    Prepare the drag region.
  779.  
  780.     tempRegion = NewRgn();
  781.     CopyRgn(dragRegion, tempRegion);
  782.     InsetRgn(tempRegion, 1, 1);
  783.     DiffRgn(dragRegion, tempRegion, dragRegion);
  784.     DisposeRgn(tempRegion);
  785.  
  786.     //    Drag the text. TrackDrag will return userCanceledErr if the drop whooshed back for any reason.
  787.  
  788.     error = TrackDrag(theDragRef, pEvent, dragRegion);
  789.     
  790.     if ((error != noErr) && (error != userCanceledErr))
  791.         return true;
  792.  
  793.     //    Check to see if the drop occurred in the Finder's Trash. If the drop occurred
  794.     //    in the Finder's Trash and a copy operation wasn't specified, delete the
  795.     //    source selection. Note that we can continute to get the attributes, drop location
  796.     //    modifiers, etc. of the drag until we dispose of it using DisposeDrag.
  797.  
  798.     GetDragAttributes(theDragRef, &attributes);
  799.     if (!(attributes & kDragInsideSenderApplication))
  800.         {
  801.         GetDropLocation(theDragRef, &dropLocation);
  802.  
  803.         GetDragModifiers(theDragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  804.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  805.  
  806.         if ((!copyText) && (IsDropInFinderTrash(&dropLocation)))
  807.             {
  808.             TEDelete(((TextDataPtr)pData)->hTE);
  809.             AdjustTE(pData, false);
  810.             AdjustScrollBars(pWindow, false, false, nil);
  811.             SetDocumentContentChanged( (WindowDataPtr) pData, true );
  812.             }
  813.  
  814.         AEDisposeDesc(&dropLocation);
  815.         }
  816.  
  817.     // Dispose of this drag, 'cause we're done.
  818.  
  819.     DisposeDrag(theDragRef);
  820.  
  821.     DisposeRgn(dragRegion);
  822.  
  823.     return true;
  824.  
  825. } // DragText
  826.